home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 March / macformat-022.iso / Shareware City / Developers / src / out-of-phase-102-c / OutOfPhase 1.02 Source / OutOfPhase Folder / TrackDisplayScheduling.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-11-23  |  37.8 KB  |  1,098 lines  |  [TEXT/KAHL]

  1. /* TrackDisplayScheduling.c */
  2. /*****************************************************************************/
  3. /*                                                                           */
  4. /*    Out Of Phase:  Digital Music Synthesis on General Purpose Computers    */
  5. /*    Copyright (C) 1994  Thomas R. Lawrence                                 */
  6. /*                                                                           */
  7. /*    This program is free software; you can redistribute it and/or modify   */
  8. /*    it under the terms of the GNU General Public License as published by   */
  9. /*    the Free Software Foundation; either version 2 of the License, or      */
  10. /*    (at your option) any later version.                                    */
  11. /*                                                                           */
  12. /*    This program is distributed in the hope that it will be useful,        */
  13. /*    but WITHOUT ANY WARRANTY; without even the implied warranty of         */
  14. /*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          */
  15. /*    GNU General Public License for more details.                           */
  16. /*                                                                           */
  17. /*    You should have received a copy of the GNU General Public License      */
  18. /*    along with this program; if not, write to the Free Software            */
  19. /*    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.              */
  20. /*                                                                           */
  21. /*    Thomas R. Lawrence can be reached at tomlaw@world.std.com.             */
  22. /*                                                                           */
  23. /*****************************************************************************/
  24.  
  25. #include "MiscInfo.h"
  26. #include "Audit.h"
  27. #include "Debug.h"
  28. #include "Definitions.h"
  29.  
  30. #include "TrackDisplayScheduling.h"
  31. #include "TrackObject.h"
  32. #include "FrameObject.h"
  33. #include "Memory.h"
  34. #include "Fractions.h"
  35. #include "DataMunging.h"
  36. #include "NoteObject.h"
  37. #include "TieTrackPixel.h"
  38. #include "TieTracking.h"
  39. #include "StaffCalibration.h"
  40.  
  41.  
  42. /* where does the very first note start (to allow insertion before it) */
  43. #define FIRSTNOTESTART (32)
  44.  
  45. typedef struct
  46.     {
  47.         long                                    PixelStart;
  48.         OrdType                                Width : 15; /* Well... >I< know it's not going to be big... */
  49.         unsigned int                    SquashThisOne : 1; /* boolean; True == don't draw this one */
  50.     } FrameAttrRec;
  51.  
  52. typedef struct
  53.     {
  54.         TrackObjectRec*                TrackObj;
  55.         long                                    NumFrames;
  56.         FrameAttrRec*                    FrameAttrArray;
  57.     } TrackAttrRec;
  58.  
  59. typedef struct
  60.     {
  61.         short                                    BarIndex : 15; /* -1 == no bar */
  62.         unsigned int                    DrawBarGreyed; /* boolean; True == draw bar greyed */
  63.     } MeasureBarInfoRec;
  64.  
  65. struct TrackDispScheduleRec
  66.     {
  67.         long                                    NumTracks;
  68.         /* the first track (TrackAttrArray[0]) is the "main" track */
  69.         TrackAttrRec*                    TrackAttrArray;
  70.         MeasureBarInfoRec*        MainTrackMeasureBars;
  71.         long                                    TotalWidth;
  72.         MyBoolean                            RecalculationRequired;
  73.         TieTrackPixelRec*            TiePixelTracker; /* NIL if not up to date */
  74.     };
  75.  
  76.  
  77. /* create a new schedule state record */
  78. TrackDispScheduleRec*    NewTrackDisplaySchedule(struct TrackObjectRec* MainTrackObj)
  79.     {
  80.         TrackDispScheduleRec*        Schedule;
  81.  
  82.         Schedule = (TrackDispScheduleRec*)AllocPtrCanFail(sizeof(TrackDispScheduleRec),
  83.             "TrackDispScheduleRec");
  84.         if (Schedule == NIL)
  85.             {
  86.              FailurePoint1:
  87.                 return NIL;
  88.             }
  89.         Schedule->TrackAttrArray = (TrackAttrRec*)AllocPtrCanFail(0,"TrackAttrRec");
  90.         if (Schedule->TrackAttrArray == NIL)
  91.             {
  92.              FailurePoint2:
  93.                 ReleasePtr((char*)Schedule);
  94.                 goto FailurePoint1;
  95.             }
  96.         Schedule->MainTrackMeasureBars = (MeasureBarInfoRec*)AllocPtrCanFail(0,
  97.             "MainTrackMeasureBars");
  98.         if (Schedule->MainTrackMeasureBars == NIL)
  99.             {
  100.              FailurePoint3:
  101.                 ReleasePtr((char*)Schedule->TrackAttrArray);
  102.                 goto FailurePoint2;
  103.             }
  104.         Schedule->NumTracks = 0;
  105.         Schedule->RecalculationRequired = True;
  106.         Schedule->TiePixelTracker = NIL;
  107.         if (!AddTrackToDisplaySchedule(Schedule,MainTrackObj))
  108.             {
  109.              FailurePoint4:
  110.                 ReleasePtr((char*)Schedule->MainTrackMeasureBars);
  111.                 goto FailurePoint3;
  112.             }
  113.         return Schedule;
  114.     }
  115.  
  116.  
  117. /* dispose of schedule state record */
  118. void                                    DisposeTrackDisplaySchedule(TrackDispScheduleRec* Schedule)
  119.     {
  120.         long                                Scan;
  121.  
  122.         CheckPtrExistence(Schedule);
  123.         if (Schedule->TiePixelTracker != NIL)
  124.             {
  125.                 DisposeTieTrackPixel(Schedule->TiePixelTracker);
  126.             }
  127.         for (Scan = 0; Scan < Schedule->NumTracks; Scan += 1)
  128.             {
  129.                 CheckPtrExistence(Schedule->TrackAttrArray[Scan].FrameAttrArray);
  130.                 ReleasePtr((char*)(Schedule->TrackAttrArray[Scan].FrameAttrArray));
  131.             }
  132.         ReleasePtr((char*)Schedule->MainTrackMeasureBars);
  133.         ReleasePtr((char*)Schedule->TrackAttrArray);
  134.         ReleasePtr((char*)Schedule);
  135.     }
  136.  
  137.  
  138. /* add track to schedule list */
  139. MyBoolean                            AddTrackToDisplaySchedule(TrackDispScheduleRec* Schedule,
  140.                                                 struct TrackObjectRec* TrackObj)
  141.     {
  142.         TrackAttrRec*                AttrList;
  143.  
  144.         CheckPtrExistence(Schedule);
  145.         CheckPtrExistence(TrackObj);
  146. #if DEBUG
  147.         {
  148.             long                                Scan;
  149.  
  150.             for (Scan = 0; Scan < Schedule->NumTracks; Scan += 1)
  151.                 {
  152.                     if (Schedule->TrackAttrArray[Scan].TrackObj == TrackObj)
  153.                         {
  154.                             PRERR(ForceAbort,"AddTrackToDisplaySchedule:  track already in list");
  155.                         }
  156.                 }
  157.         }
  158. #endif
  159.         AttrList = (TrackAttrRec*)ResizePtr((char*)Schedule->TrackAttrArray,
  160.             (Schedule->NumTracks + 1) * sizeof(TrackAttrRec));
  161.         if (AttrList == NIL)
  162.             {
  163.              FailurePoint1:
  164.                 return False;
  165.             }
  166.         Schedule->TrackAttrArray = AttrList;
  167.         Schedule->TrackAttrArray[Schedule->NumTracks].TrackObj = TrackObj;
  168.         Schedule->TrackAttrArray[Schedule->NumTracks].NumFrames = 0;
  169.         Schedule->TrackAttrArray[Schedule->NumTracks].FrameAttrArray
  170.             = (FrameAttrRec*)AllocPtrCanFail(0,"FrameAttrRec");
  171.         if (Schedule->TrackAttrArray[Schedule->NumTracks].FrameAttrArray == NIL)
  172.             {
  173.                 goto FailurePoint1;
  174.             }
  175.         Schedule->NumTracks += 1;
  176.         Schedule->RecalculationRequired = True;
  177.         return True;
  178.     }
  179.  
  180.  
  181. /* delete a track from the schedule list */
  182. void                                    DeleteTrackFromDisplaySchedule(TrackDispScheduleRec* Schedule,
  183.                                                 struct TrackObjectRec* TrackObj)
  184.     {
  185.         long                                Index;
  186.  
  187.         CheckPtrExistence(Schedule);
  188.         CheckPtrExistence(TrackObj);
  189.         Index = 0;
  190.         /* find the entry being deleted */
  191.         while (Schedule->TrackAttrArray[Index].TrackObj != TrackObj)
  192.             {
  193.                 /* this bounds check is actually after the fact, for the conditional above */
  194.                 PRNGCHK(Schedule->TrackAttrArray,&(Schedule->TrackAttrArray[Index]),
  195.                     sizeof(Schedule->TrackAttrArray[Index]));
  196.                 Index += 1;
  197.                 ERROR(Index >= Schedule->NumTracks,PRERR(ForceAbort,
  198.                     "DeleteTrackFromDisplaySchedule:  track unknown"));
  199.             }
  200.         ERROR(Index == 0,PRERR(ForceAbort,
  201.             "DeleteTrackFromDisplaySchedule:  deleting the main track"));
  202.         /* delete the actual array */
  203.         ReleasePtr((char*)Schedule->TrackAttrArray[Index].FrameAttrArray);
  204.         /* shift the elements in the array after the hole down.  This will leave */
  205.         /* empty slots at the end of the array but... who cares? */
  206.         while (Index < Schedule->NumTracks - 1)
  207.             {
  208.                 Schedule->TrackAttrArray[Index] = Schedule->TrackAttrArray[Index + 1];
  209.                 Index += 1;
  210.             }
  211.         /* decrement the extents counter */
  212.         Schedule->NumTracks -= 1;
  213.         /* mark thing ready for total recalculation */
  214.         Schedule->RecalculationRequired = True;
  215.     }
  216.  
  217.  
  218. /* note location information must be available by the time this is called. */
  219. static void                        BuildTieRepresentation(TrackDispScheduleRec* Schedule)
  220.     {
  221.         long                                FrameLimit;
  222.         long                                FrameScan;
  223.         TrackObjectRec*            TrackObj;
  224.         TieTrackRec*                TieTracker;
  225.  
  226.         CheckPtrExistence(Schedule);
  227.         PRNGCHK(Schedule->TrackAttrArray,&(Schedule->TrackAttrArray[0]),
  228.             sizeof(Schedule->TrackAttrArray[0]));
  229.         TrackObj = Schedule->TrackAttrArray[0].TrackObj;
  230.         CheckPtrExistence(TrackObj);
  231.         ERROR(Schedule->TiePixelTracker == NIL,PRERR(ForceAbort,
  232.             "BuildTieRepresentation:  TiePixelTracker is NIL"));
  233.         CheckPtrExistence(Schedule->TiePixelTracker);
  234.  
  235.         TieTracker = NewTieTracker();
  236.         if (TieTracker == NIL)
  237.             {
  238.                 /* oh, well, all the ties just disappeared. */
  239.                 return;
  240.             }
  241.  
  242.         FrameLimit = TrackObjectGetNumFrames(TrackObj);
  243.         for (FrameScan = 0; FrameScan < FrameLimit; FrameScan += 1)
  244.             {
  245.                 FrameObjectRec*            Frame;
  246.  
  247.                 Frame = TrackObjectGetFrame(TrackObj,FrameScan);
  248.                 if (!IsThisACommandFrame(Frame))
  249.                     {
  250.                         long                                NoteScan;
  251.                         long                                NoteLimit;
  252.  
  253.                         NoteLimit = NumNotesInFrame(Frame);
  254.                         for (NoteScan = 0; NoteScan < NoteLimit; NoteScan += 1)
  255.                             {
  256.                                 NoteObjectRec*            Note;
  257.                                 NoteObjectRec*            TieTargetNote;
  258.                                 MyBoolean                        ThereIsATieToThisNote;
  259.                                 long                                PreviousTiePixelX;
  260.                                 long                                PreviousTiePixelY;
  261.                                 NoteObjectRec*            PreviousTieNote;
  262.  
  263.                                 Note = GetNoteFromFrame(Frame,NoteScan);
  264.  
  265.                                 /* get any note it ties to */
  266.                                 TieTargetNote = GetNoteTieTarget(Note);
  267.  
  268.                                 /* get any note tied to it */
  269.                                 ThereIsATieToThisNote = GetTieSourceFromDestination(TieTracker,
  270.                                     &PreviousTiePixelX,&PreviousTiePixelY,&PreviousTieNote,Note);
  271.  
  272.                                 /* now, deal with all of this jucy information */
  273.                                 /* first, get the location of this note, if needed */
  274.                                 if ((TieTargetNote != NIL) || ThereIsATieToThisNote)
  275.                                     {
  276.                                         long                                ThisNotePixelX;
  277.                                         long                                ThisNotePixelY;
  278.                                         EXECUTE(MyBoolean        CallResult;)
  279.  
  280.                                         /* gather location information */
  281.                                         EXECUTE(CallResult = )TrackDisplayIndexToPixel(Schedule,
  282.                                             0/*main track*/,FrameScan,&ThisNotePixelX);
  283.                                         ThisNotePixelX += NoteScan * INTERNALSEPARATION;
  284.                                         ERROR(!CallResult,PRERR(ForceAbort,
  285.                                             "BuildTieRepresentation:  TrackDisplayIndexToPixel failed"));
  286.                                         ThisNotePixelY = ConvertPitchToPixel(GetNotePitch(Note),
  287.                                             GetNoteFlatOrSharpStatus(Note));
  288.  
  289.                                         /* do the thing for a note tied to us */
  290.                                         if (ThereIsATieToThisNote)
  291.                                             {
  292.                                                 AddTieTrackPixelElement(Schedule->TiePixelTracker,
  293.                                                     PreviousTiePixelX,PreviousTiePixelY,ThisNotePixelX,
  294.                                                     ThisNotePixelY);
  295.                                             }
  296.  
  297.                                         /* do the thing for us tied to another note */
  298.                                         if (TieTargetNote != NIL)
  299.                                             {
  300.                                                 AddTiePairToTieTracker(TieTracker,Note,
  301.                                                     ThisNotePixelX,ThisNotePixelY,TieTargetNote);
  302.                                             }
  303.                                     }
  304.                             }
  305.                     }
  306.             }
  307.  
  308.         DisposeTieTracker(TieTracker);
  309.     }
  310.  
  311.  
  312. typedef struct
  313.     {
  314.         long                                    CurrentIndex;
  315.         FractionRec                        CurrentFrameStartTime;
  316.         FractionRec                        CurrentFrameDuration;
  317.     } TrackWorkRec;
  318.  
  319.  
  320. #ifdef THINK_C
  321.     /* THINK C's optimizer has some peculiar problems with overlaying variables */
  322.     /* and using the wrong size to access compiler temporaries when compiling */
  323.     /* this routine.  See the bit where MaximumWidth is adjusted if it is less */
  324.     /* than the width of the frame being considered. */
  325.     #if __option(global_optimizer)
  326.         #define THINK_C_GLOBAL_OPTIMIZER_ENABLED (1)
  327.         #pragma options(!global_optimizer)
  328.     #else
  329.         #define THINK_C_GLOBAL_OPTIMIZER_ENABLED (0)
  330.     #endif
  331. #endif
  332.  
  333.  
  334. /* apply schedule to tracks. */
  335. MyBoolean                            TrackDisplayScheduleUpdate(TrackDispScheduleRec* Schedule)
  336.     {
  337.         long                                Scan;
  338.         long                                CurrentXLocation;
  339.         FractionRec                    CurrentTime;
  340.         TrackWorkRec*                WorkArray;
  341.         MyBoolean                        DoneFlag;
  342.         MeasureBarInfoRec*    NewMeasureBarArray;
  343.         FractionRec                    MeasureBarIntervalThing;
  344.         FractionRec                    MeasureBarWidth;
  345.         long                                MeasureBarIndex;
  346.  
  347.         CheckPtrExistence(Schedule);
  348.  
  349.         /* if everything is up to date, then don't bother */
  350.         if (!Schedule->RecalculationRequired)
  351.             {
  352.                 return True;
  353.             }
  354.  
  355.         if (Schedule->TiePixelTracker != NIL)
  356.             {
  357.                 DisposeTieTrackPixel(Schedule->TiePixelTracker);
  358.                 Schedule->TiePixelTracker = NIL;
  359.             }
  360.         Schedule->TiePixelTracker = NewTieTrackPixel();
  361.         if (Schedule->TiePixelTracker == NIL)
  362.             {
  363.                 return False;
  364.             }
  365.  
  366.         /* resize the measure bar table */
  367.         Scan = TrackObjectGetNumFrames(Schedule->TrackAttrArray[0].TrackObj);
  368.         NewMeasureBarArray = (MeasureBarInfoRec*)ResizePtr(
  369.             (char*)Schedule->MainTrackMeasureBars,sizeof(MeasureBarInfoRec) * Scan);
  370.         if (NewMeasureBarArray == NIL)
  371.             {
  372.                 return False;
  373.             }
  374.         Schedule->MainTrackMeasureBars = NewMeasureBarArray;
  375.         while (Scan > 0)
  376.             {
  377.                 /* erase the entries in the table */
  378.                 Scan -= 1;
  379.                 Schedule->MainTrackMeasureBars[Scan].BarIndex = -1; /* no bar */
  380.             }
  381.  
  382.         /* resize the track location tables */
  383.         for (Scan = 0; Scan < Schedule->NumTracks; Scan += 1)
  384.             {
  385.                 long                                TheirFramesTemp;
  386.  
  387.                 TheirFramesTemp = TrackObjectGetNumFrames(
  388.                     Schedule->TrackAttrArray[Scan].TrackObj);
  389.                 if (Schedule->TrackAttrArray[Scan].NumFrames != TheirFramesTemp)
  390.                     {
  391.                         FrameAttrRec*                NewArray;
  392.                         long                                Index;
  393.  
  394.                         /* resize the array */
  395.                         PRNGCHK(Schedule->TrackAttrArray,&(Schedule->TrackAttrArray[Scan]),
  396.                             sizeof(Schedule->TrackAttrArray[Scan]));
  397.                         CheckPtrExistence(Schedule->TrackAttrArray[Scan].FrameAttrArray);
  398.                         NewArray = (FrameAttrRec*)ResizePtr((char*)(Schedule->TrackAttrArray
  399.                             [Scan].FrameAttrArray),sizeof(FrameAttrRec) * TheirFramesTemp);
  400.                         if (NewArray == NIL)
  401.                             {
  402.                                 return False;
  403.                             }
  404.                         Schedule->TrackAttrArray[Scan].FrameAttrArray = NewArray;
  405.  
  406.                         /* zero the new space in the array so that we make sure it changes */
  407.                         for (Index = Schedule->TrackAttrArray[Scan].NumFrames;
  408.                             Index < TheirFramesTemp; Index += 1)
  409.                             {
  410.                                 PRNGCHK(Schedule->TrackAttrArray[Scan].FrameAttrArray,
  411.                                     &(Schedule->TrackAttrArray[Scan].FrameAttrArray[Index]),
  412.                                     sizeof(Schedule->TrackAttrArray[Scan].FrameAttrArray[Index]));
  413.                                 Schedule->TrackAttrArray[Scan].FrameAttrArray[Index].PixelStart = -1;
  414.                                 Schedule->TrackAttrArray[Scan].FrameAttrArray[Index].Width = -1;
  415.                                 Schedule->TrackAttrArray[Scan].FrameAttrArray[Index].SquashThisOne = False;
  416.                             }
  417.  
  418.                         /* update size number */
  419.                         Schedule->TrackAttrArray[Scan].NumFrames = TheirFramesTemp;
  420.                     }
  421.             }
  422.  
  423.  
  424.         /* initialize local variable counters */
  425.         CurrentXLocation = FIRSTNOTESTART;
  426.         CurrentTime.Integer = 0;
  427.         CurrentTime.Fraction = 0;
  428.         CurrentTime.Denominator = (64*3*5*7*2);
  429.  
  430.         MeasureBarIntervalThing.Integer = 0;
  431.         MeasureBarIntervalThing.Fraction = 0;
  432.         MeasureBarIntervalThing.Denominator = (64*3*5*7*2);
  433.         MeasureBarWidth.Integer = 1;
  434.         MeasureBarWidth.Fraction = 0;
  435.         MeasureBarWidth.Denominator = (64*3*5*7*2);
  436.         MeasureBarIndex = 1;
  437.  
  438.  
  439.         /* build workspace for each track */
  440.         WorkArray = (TrackWorkRec*)AllocPtrCanFail(sizeof(TrackWorkRec)
  441.             * Schedule->NumTracks,"TrackWorkRec");
  442.         if (WorkArray == NIL)
  443.             {
  444.                 return False;
  445.             }
  446.         for (Scan = 0; Scan < Schedule->NumTracks; Scan += 1)
  447.             {
  448.                 WorkArray[Scan].CurrentIndex = 0;
  449.                 WorkArray[Scan].CurrentFrameStartTime.Integer = 0;
  450.                 WorkArray[Scan].CurrentFrameStartTime.Fraction = 0;
  451.                 WorkArray[Scan].CurrentFrameStartTime.Denominator = (64*3*5*7*2);
  452.             }
  453.  
  454.  
  455.         /* perform scheduling sweep */
  456.         /* how it works: */
  457.         /* the start time and duration of the next frame in each channel are */
  458.         /* calculated.  If the start time is NOT in the future, but is now, then */
  459.         /* the note can be scheduled.  When it is scheduled, it's start time is */
  460.         /* advanced by adding it's duration.  This way, notes will be scheduled */
  461.         /* for display on the screen in the same order that they will be played. */
  462.         do
  463.             {
  464.                 long                                MaximumWidth;
  465.  
  466.  
  467.                 /* stage 1:  calculate duration of next event for all frames */
  468.                 /* This also figures out what the smallest duration is */
  469.                 /* After stage 1, Frame will be NIL if the channel is not scheduled */
  470.                 /* this time around, or valid if it contains the one to schedule */
  471.                 MaximumWidth = 0;
  472.                 for (Scan = 0; Scan < Schedule->NumTracks; Scan += 1)
  473.                     {
  474.  
  475.                         if (WorkArray[Scan].CurrentIndex
  476.                             < Schedule->TrackAttrArray[Scan].NumFrames)
  477.                             {
  478.                                 /* this channel still has more items to schedule. */
  479.                                 /* we want to see if this frame will be scheduled this time. */
  480.                                 ERROR(FracGreaterThan(&CurrentTime,&(WorkArray[Scan]
  481.                                     .CurrentFrameStartTime)),PRERR(AllowResume,
  482.                                     "TrackDisplayScheduleUpdate:  current time later than frame start"));
  483.  
  484.                                 if (FractionsEqual(&CurrentTime,&(WorkArray[Scan].CurrentFrameStartTime)))
  485.                                     {
  486.                                         FrameObjectRec*            Frame;
  487.  
  488.                                         /* the frame is scheduled this time around */
  489.  
  490.                                         /* obtain the frame reference */
  491.                                         Frame = TrackObjectGetFrame(Schedule->
  492.                                             TrackAttrArray[Scan].TrackObj,WorkArray[Scan].CurrentIndex);
  493.  
  494.                                         /* get duration of this frame */
  495.                                         DurationOfFrame(Frame,&(WorkArray[Scan].CurrentFrameDuration));
  496.  
  497.                                         /* decide whether the measure bar flag should be set. */
  498.                                         /* this is only done for main track, hence "Scan == 0". */
  499.                                         if (Scan == 0)
  500.                                             {
  501.                                                 /* for big notes (like quads) this may increment */
  502.                                                 /* MeasureBarIndex several times, but that's the desired */
  503.                                                 /* behavior. */
  504.                                                 while (FracGreaterEqual(&MeasureBarIntervalThing,
  505.                                                     &MeasureBarWidth))
  506.                                                     {
  507.                                                         MyBoolean                    GreyedFlag;
  508.  
  509.                                                         /* if the current measure bar index is greater than the */
  510.                                                         /* measure bar interval, then set the flag & decrement */
  511.                                                         /* the measure bar interval. */
  512.                                                         /*  MeasureBarWidth = num whole notes in a measure */
  513.                                                         /*  MeasureBarIntervalThing = current accumulated notes */
  514.                                                         GreyedFlag = !FractionsEqual(&MeasureBarIntervalThing,
  515.                                                             &MeasureBarWidth);
  516.                                                         SubFractions(&MeasureBarIntervalThing,&MeasureBarWidth,
  517.                                                             &MeasureBarIntervalThing);
  518.                                                         PRNGCHK(Schedule->MainTrackMeasureBars,&(Schedule
  519.                                                             ->MainTrackMeasureBars[WorkArray[Scan].CurrentIndex]),
  520.                                                             sizeof(Schedule->MainTrackMeasureBars[WorkArray[Scan]
  521.                                                             .CurrentIndex]));
  522.                                                         Schedule->MainTrackMeasureBars[WorkArray[Scan]
  523.                                                             .CurrentIndex].BarIndex = MeasureBarIndex;
  524.                                                         Schedule->MainTrackMeasureBars[WorkArray[Scan]
  525.                                                             .CurrentIndex].DrawBarGreyed = GreyedFlag;
  526.                                                         MeasureBarIndex += 1;
  527.                                                     }
  528.  
  529.                                                 /* now, increment the value with the current note */
  530.                                                 AddFractions(&MeasureBarIntervalThing,&(WorkArray[Scan]
  531.                                                     .CurrentFrameDuration),&MeasureBarIntervalThing);
  532.  
  533.                                                 /* finally, if the note is a meter adjust command, use */
  534.                                                 /* it to recalibrate the measure barring */
  535.                                                 if (IsThisACommandFrame(Frame))
  536.                                                     {
  537.                                                         NoteObjectRec*            MaybeMeterCmd;
  538.  
  539.                                                         MaybeMeterCmd = GetNoteFromFrame(Frame,0);
  540.                                                         if (GetCommandOpcode(MaybeMeterCmd) == eCmdSetMeter)
  541.                                                             {
  542.                                                                 /* set the meter.  this is used by the editor for */
  543.                                                                 /* placing measure bars.  measuring restarts */
  544.                                                                 /* immediately after this command */
  545.                                                                 /* <1i> = numerator, <2i> = denominator */
  546.                                                                 if (GetCommandNumericArg2(MaybeMeterCmd) >= 1)
  547.                                                                     {
  548.                                                                         /* make sure denominator is within range */
  549.                                                                         if (GetCommandNumericArg1(MaybeMeterCmd) >= 1)
  550.                                                                             {
  551.                                                                                 /* set the new measure width */
  552.                                                                                 MeasureBarWidth.Denominator
  553.                                                                                     = GetCommandNumericArg2(MaybeMeterCmd);
  554.                                                                                 MeasureBarWidth.Fraction
  555.                                                                                     = GetCommandNumericArg1(MaybeMeterCmd)
  556.                                                                                         % MeasureBarWidth.Denominator;
  557.                                                                                 MeasureBarWidth.Integer
  558.                                                                                     = GetCommandNumericArg1(MaybeMeterCmd)
  559.                                                                                         / MeasureBarWidth.Denominator;
  560.  
  561.                                                                                 /* reset the current index */
  562.                                                                                 MeasureBarIntervalThing.Denominator = (64*3*5*7*2);
  563.                                                                                 MeasureBarIntervalThing.Fraction = 0;
  564.                                                                                 MeasureBarIntervalThing.Integer = 0;
  565.                                                                             }
  566.                                                                     }
  567.                                                             }
  568.                                                         else if (GetCommandOpcode(MaybeMeterCmd)
  569.                                                             == eCmdSetMeasureNumber)
  570.                                                             {
  571.                                                                 MeasureBarIndex = GetCommandNumericArg1(MaybeMeterCmd);
  572.                                                             }
  573.                                                     }
  574.                                             }
  575.  
  576.                                         /* now that we have the frame's duration and have it scheduled */
  577.                                         /* for display (Frame), increment the start */
  578.                                         /* time to the end of the frame (which is start of next frame) */
  579.                                         PRNGCHK(WorkArray,&(WorkArray[Scan]),sizeof(WorkArray[Scan]));
  580.                                         AddFractions(&(WorkArray[Scan].CurrentFrameDuration),
  581.                                             &(WorkArray[Scan].CurrentFrameStartTime),
  582.                                             &(WorkArray[Scan].CurrentFrameStartTime));
  583.  
  584.                                         if ((Scan == 0) || !IsThisACommandFrame(Frame))
  585.                                             {
  586.                                                 /* if this is NOT a command frame, then it should be scheduled */
  587.                                                 /* normally for display. */
  588.  
  589.                                                 /* set up the drawing attributes */
  590.                                                 PRNGCHK(Schedule->TrackAttrArray[Scan].FrameAttrArray,
  591.                                                     &(Schedule->TrackAttrArray[Scan].FrameAttrArray[WorkArray[Scan]
  592.                                                     .CurrentIndex]),sizeof(Schedule->TrackAttrArray[Scan]
  593.                                                     .FrameAttrArray[WorkArray[Scan].CurrentIndex]));
  594.  
  595.                                                 Schedule->TrackAttrArray[Scan].FrameAttrArray[
  596.                                                     WorkArray[Scan].CurrentIndex].PixelStart = CurrentXLocation;
  597.                                                 Schedule->TrackAttrArray[Scan].FrameAttrArray[
  598.                                                     WorkArray[Scan].CurrentIndex].Width = WidthOfFrameAndDraw(
  599.                                                     0,0,0,0,0,0,Frame,False/*don'tdraw*/,False);
  600.                                                 Schedule->TrackAttrArray[Scan].FrameAttrArray[
  601.                                                     WorkArray[Scan].CurrentIndex].SquashThisOne = False;
  602.  
  603.                                                 ERROR(Schedule->TrackAttrArray[Scan].FrameAttrArray[
  604.                                                     WorkArray[Scan].CurrentIndex].Width == 0,PRERR(AllowResume,
  605.                                                     "TrackDisplayScheduleUpdate:  frame's width is 0"));
  606.  
  607.                                                 /* we need to obtain the maximum width of these frame so we */
  608.                                                 /* can figure out what the next X location will be */
  609.                                                 if (Schedule->TrackAttrArray[Scan].FrameAttrArray[
  610.                                                     WorkArray[Scan].CurrentIndex].Width > MaximumWidth)
  611.                                                     {
  612.                                                         MaximumWidth = Schedule->TrackAttrArray[Scan]
  613.                                                             .FrameAttrArray[WorkArray[Scan].CurrentIndex].Width;
  614.                                                     }
  615.                                             }
  616.                                          else
  617.                                             {
  618.                                                 /* otherwise this IS a command frame in a non-main track */
  619.                                                 /* and therefore it should not be scheduled */
  620.  
  621.                                                 /* adjust the displaylocation parameters */
  622.                                                 PRNGCHK(Schedule->TrackAttrArray[Scan].FrameAttrArray,
  623.                                                     &(Schedule->TrackAttrArray[Scan].FrameAttrArray[WorkArray[Scan]
  624.                                                     .CurrentIndex]),sizeof(Schedule->TrackAttrArray[Scan]
  625.                                                     .FrameAttrArray[WorkArray[Scan].CurrentIndex]));
  626.  
  627.                                                 Schedule->TrackAttrArray[Scan].FrameAttrArray[
  628.                                                     WorkArray[Scan].CurrentIndex].PixelStart = CurrentXLocation;
  629.                                                 Schedule->TrackAttrArray[Scan].FrameAttrArray[
  630.                                                     WorkArray[Scan].CurrentIndex].Width = 0; /* no width! */
  631.                                                 Schedule->TrackAttrArray[Scan].FrameAttrArray[
  632.                                                     WorkArray[Scan].CurrentIndex].SquashThisOne = True; /*squish*/
  633.                                             }
  634.  
  635.                                         /* advance frame pointer to the next frame in the channel */
  636.                                         WorkArray[Scan].CurrentIndex += 1;
  637.                                     }
  638.                             }
  639.                     }
  640.                 /* advance current X position using biggest width at this position */
  641.                 if (MaximumWidth != 0)
  642.                     {
  643.                         /* MaximumWidth != 0 only happens if some stuff was scheduled */
  644.                         CurrentXLocation += MaximumWidth + EXTERNALSEPARATION;
  645.                     }
  646.  
  647.  
  648.                 /* stage 2:  see if we are totally done with the tracks */
  649.                 DoneFlag = True;
  650.                 for (Scan = 0; Scan < Schedule->NumTracks; Scan += 1)
  651.                     {
  652.                         if (WorkArray[Scan].CurrentIndex
  653.                             < Schedule->TrackAttrArray[Scan].NumFrames)
  654.                             {
  655.                                 /* this channel still has more items to schedule. */
  656.                                 DoneFlag = False;
  657.                             }
  658.                     }
  659.  
  660.  
  661.                 /* stage 3:  advance the current time counter to point to the start time */
  662.                 /* of the closest possible next frame, which corresponds to the soonest one */
  663.                 /* immediately after the smallest frame that we just scheduled. */
  664.                 /* if we aren't done, figure out when the soonest next frame starts */
  665.                 if (!DoneFlag)
  666.                     {
  667.                         FractionRec                    SmallestDelta;
  668.                         MyBoolean                        SmallestDeltaValid;
  669.  
  670.                         /* but there's still more to go, so advance the time counter to */
  671.                         /* the closest possible next frame. */
  672.                         /* This will happen if a very small note ends a track.  The duration */
  673.                         /* of the small note will be added, but since the track is done, */
  674.                         /* there might not be any note whatsoever at that time.  In this */
  675.                         /* case, we need to find when the next note will be. */
  676.                         /* In order to do this, we search for the soonest possible frame */
  677.                         /* that we can find that is past CurrentTime. */
  678.                         SmallestDeltaValid = False;
  679.                         for (Scan = 0; Scan < Schedule->NumTracks; Scan += 1)
  680.                             {
  681.                                 if (WorkArray[Scan].CurrentIndex
  682.                                     < Schedule->TrackAttrArray[Scan].NumFrames)
  683.                                     {
  684.                                         FractionRec                DeltaTemp;
  685.  
  686.                                         /* start time of next frame should NEVER be less than */
  687.                                         /* current time. */
  688.                                         ERROR(FracGreaterEqual(&CurrentTime,&(WorkArray[Scan]
  689.                                             .CurrentFrameStartTime)) && !FractionsEqual(&CurrentTime,
  690.                                             &(WorkArray[Scan].CurrentFrameStartTime)),PRERR(ForceAbort,
  691.                                             "TrackDisplayScheduleUpdate:  next less than current time"));
  692.  
  693.                                         /* figure out difference between then and now */
  694.                                         SubFractions(&(WorkArray[Scan].CurrentFrameStartTime),
  695.                                             &CurrentTime,&DeltaTemp);
  696.                                         if (!SmallestDeltaValid
  697.                                             || FracGreaterThan(&SmallestDelta,&DeltaTemp))
  698.                                             {
  699.                                                 SmallestDelta = DeltaTemp;
  700.                                                 SmallestDeltaValid = True;
  701.                                             }
  702.                                     }
  703.                             }
  704.  
  705.  
  706.                         /* if SmallestDeltaValid is False at this point, then there aren't */
  707.                         /* any things that can be done, which should never happen. */
  708.                         ERROR(!SmallestDeltaValid,PRERR(AllowResume,
  709.                             "TrackDisplayScheduleUpdate:  no track, but DoneFlag is false"));
  710.                         AddFractions(&SmallestDelta,&CurrentTime,&CurrentTime);
  711.                     }
  712.  
  713.                 /* make sure things don't get out of hand.  this magic number is derived */
  714.                 /* from the denominator of the smallest possible note:  64th note, with */
  715.                 /* a possible 3, 5, or 7 division, and (3/2) dot. */
  716.                 ERROR(CurrentTime.Denominator > 64*3*5*7*2,PRERR(ForceAbort,
  717.                     "TrackDisplayScheduleUpdate:  factoring malfunction"));
  718.             } while (!DoneFlag);
  719.         Schedule->TotalWidth = CurrentXLocation;
  720.         Schedule->RecalculationRequired = False;
  721.         ReleasePtr((char*)WorkArray);
  722.  
  723.         /* build tie representation */
  724.         BuildTieRepresentation(Schedule);
  725.  
  726.         return True;
  727.     }
  728.  
  729.  
  730. #ifdef THINK_C
  731.     #if THINK_C_GLOBAL_OPTIMIZER_ENABLED
  732.         #pragma options(global_optimizer)
  733.     #endif
  734. #endif
  735.  
  736.  
  737. /* find out where a pixel position is located in the score.  it returns the */
  738. /* index of the frame the position is on and *OnFrame is True if it was on a frame. */
  739. /* otherwise, it returns the index of the frame that the position precedes and */
  740. /* sets *OnFrame to False.  TrackObj is the track that we want to find positions */
  741. /* with respect to. */
  742. MyBoolean                            TrackDisplayPixelToIndex(TrackDispScheduleRec* Schedule,
  743.                                                 struct TrackObjectRec* TrackObj, long PixelPosition,
  744.                                                 MyBoolean* OnFrame, long* Index)
  745.     {
  746.         FrameAttrRec*                FrameAttrArray;
  747.         long                                Scan;
  748.         long                                Limit;
  749.         long                                LeftIndex;
  750.         long                                RightIndex;
  751.  
  752.         CheckPtrExistence(Schedule);
  753.         CheckPtrExistence(TrackObj);
  754.         if (!TrackDisplayScheduleUpdate(Schedule))
  755.             {
  756.                 return False;
  757.             }
  758.         for (Scan = 0; Scan < Schedule->NumTracks; Scan += 1)
  759.             {
  760.                 if (TrackObj == Schedule->TrackAttrArray[Scan].TrackObj)
  761.                     {
  762.                         FrameAttrArray = Schedule->TrackAttrArray[Scan].FrameAttrArray;
  763.                         CheckPtrExistence(FrameAttrArray);
  764.                         Limit = Schedule->TrackAttrArray[Scan].NumFrames;
  765.                         goto FoundTrackPoint;
  766.                     }
  767.             }
  768.         EXECUTE(PRERR(ForceAbort,"TrackDisplayPixelToIndex:  unknown track specified"));
  769.      FoundTrackPoint:
  770.         /* perform a binary search to locate the cell responsible for the track */
  771.         /* first, check to make sure index is within range */
  772.         if (PixelPosition < 0)
  773.             {
  774.                 *OnFrame = False;
  775.                 *Index = 0;
  776.                 return True;
  777.             }
  778.         if ((Limit == 0) || (PixelPosition >= FrameAttrArray[Limit - 1].PixelStart
  779.             + FrameAttrArray[Limit - 1].Width))
  780.             {
  781.                 /* since we are beyond the end, we set *OnFrame false to indicate that */
  782.                 /* the pixel position given is not a valid frame, and we return the index */
  783.                 /* of the [nonexistent] next frame */
  784.                 *OnFrame = False;
  785.                 *Index = Limit;
  786.                 return True;
  787.             }
  788.         /* initialize counter variables.  Invariant:  Limit > 0 */
  789.         LeftIndex = 0;
  790.         RightIndex = Limit - 1;
  791.         while (LeftIndex != RightIndex)
  792.             {
  793.                 long                                Midpoint;
  794.  
  795.                 /* find out pivot point for comparison */
  796.                 Midpoint = (LeftIndex + RightIndex) / 2;
  797.                 /* check to see if index falls on pivot frame */
  798.                 PRNGCHK(FrameAttrArray,&(FrameAttrArray[Midpoint]),
  799.                     sizeof(FrameAttrArray[Midpoint]));
  800.                 if ((FrameAttrArray[Midpoint].PixelStart <= PixelPosition)
  801.                     && (FrameAttrArray[Midpoint].PixelStart
  802.                     + FrameAttrArray[Midpoint].Width > PixelPosition))
  803.                     {
  804.                         *OnFrame = True;
  805.                         *Index = Midpoint;
  806.                         return True;
  807.                     }
  808.                 /* check to see if we can discard left half */
  809.                 if (FrameAttrArray[Midpoint].PixelStart <= PixelPosition)
  810.                     {
  811.                         LeftIndex = Midpoint + 1;
  812.                     }
  813.                 /* check to see if we can discard right half */
  814.                 if (FrameAttrArray[Midpoint].PixelStart > PixelPosition)
  815.                     {
  816.                         RightIndex = Midpoint;
  817.                     }
  818.             }
  819.         /* invariant: LeftIndex == RightIndex */
  820.         /* now figure out what the results mean */
  821.         if (FrameAttrArray[LeftIndex].PixelStart > PixelPosition)
  822.             {
  823.                 /* it is before the one we found, in the interstice. */
  824.                 *OnFrame = False;
  825.                 *Index = LeftIndex;
  826.                 return True;
  827.             }
  828.         /* otherwise, it must be on the one we found */
  829.         ERROR(FrameAttrArray[LeftIndex].PixelStart + FrameAttrArray[LeftIndex].Width
  830.             < PixelPosition,PRERR(ForceAbort,"TrackDisplayPixelToIndex:  error"));
  831.         *OnFrame = True;
  832.         *Index = LeftIndex;
  833.         return True;
  834.     }
  835.  
  836.  
  837. /* mark scheduler so that it recalculates all the stuff.  the track and frame */
  838. /* index specify where recalculation has to start from, so that data before that */
  839. /* doesn't need to be updated. */
  840. void                                    TrackDisplayScheduleMarkChanged(TrackDispScheduleRec* Schedule,
  841.                                                 struct TrackObjectRec* TrackObj, long FrameIndex)
  842.     {
  843.         CheckPtrExistence(Schedule);
  844.         CheckPtrExistence(TrackObj);
  845.         /* we ignore starting position parameters for now. */
  846.         Schedule->RecalculationRequired = True;
  847.     }
  848.  
  849.  
  850. /* calculate the pixel index for a frame based on it's array index */
  851. MyBoolean                            TrackDisplayIndexToPixel(TrackDispScheduleRec* Schedule,
  852.                                                 long TrackIndex, long FrameIndex, long* Pixel)
  853.     {
  854.         CheckPtrExistence(Schedule);
  855.         ERROR((TrackIndex < 0) || (TrackIndex >= TrackDisplayGetNumTracks(Schedule)),
  856.             PRERR(ForceAbort,"TrackDisplayIndexToPixel:  track index out of range"));
  857.         if (!TrackDisplayScheduleUpdate(Schedule))
  858.             {
  859.                 return False;
  860.             }
  861.         PRNGCHK(Schedule->TrackAttrArray[TrackIndex].FrameAttrArray,
  862.             &(Schedule->TrackAttrArray[TrackIndex].FrameAttrArray[FrameIndex]),
  863.             sizeof(Schedule->TrackAttrArray[TrackIndex].FrameAttrArray[FrameIndex]));
  864.         *Pixel = Schedule->TrackAttrArray[TrackIndex].FrameAttrArray[FrameIndex].PixelStart;
  865.         return True;
  866.     }
  867.  
  868.  
  869. /* get the number of tracks controlled by this scheduler */
  870. long                                    TrackDisplayGetNumTracks(TrackDispScheduleRec* Schedule)
  871.     {
  872.         CheckPtrExistence(Schedule);
  873.         return Schedule->NumTracks;
  874.     }
  875.  
  876.  
  877. /* look up the track index of a specific track */
  878. long                                    TrackDisplayGetTrackIndex(TrackDispScheduleRec* Schedule,
  879.                                                 struct TrackObjectRec* TrackObj)
  880.     {
  881.         long                                Scan;
  882.  
  883.         CheckPtrExistence(Schedule);
  884.         Scan = 0;
  885.         while (Scan < Schedule->NumTracks)
  886.             {
  887.                 if (Schedule->TrackAttrArray[Scan].TrackObj == TrackObj)
  888.                     {
  889.                         return Scan;
  890.                     }
  891.                 Scan += 1;
  892.             }
  893.         EXECUTE(PRERR(ForceAbort,"TrackDisplayGetTrackIndex:  unknown track"));
  894.     }
  895.  
  896.  
  897. /* get the track specified by the index (0..last func return value - 1) */
  898. TrackObjectRec*                TrackDisplayGetParticularTrack(TrackDispScheduleRec* Schedule,
  899.                                                 long Index)
  900.     {
  901.         CheckPtrExistence(Schedule);
  902.         PRNGCHK(Schedule->TrackAttrArray,&(Schedule->TrackAttrArray[Index].TrackObj),
  903.             sizeof(TrackObjectRec*));
  904.         return Schedule->TrackAttrArray[Index].TrackObj;
  905.     }
  906.  
  907.  
  908. /* get the total length of the track */
  909. MyBoolean                            TrackDisplayGetTotalLength(TrackDispScheduleRec* Schedule,
  910.                                                 long* LongestLength)
  911.     {
  912.         CheckPtrExistence(Schedule);
  913.         if (!TrackDisplayScheduleUpdate(Schedule))
  914.             {
  915.                 return False;
  916.             }
  917.         *LongestLength = Schedule->TotalWidth;
  918.         return True;
  919.     }
  920.  
  921.  
  922. /* find out what NOTE (not frame) is at the specified location.  NIL */
  923. /* is returned if no note is there.  If the flag is clear, then it's a note, */
  924. /* otherwise it's a command.  If you intend to modify it, then pass an address */
  925. /* for FrameIndex so you know what to pass to TrackDisplayScheduleMarkChanged. */
  926. struct NoteObjectRec*    TrackDisplayGetUnderlyingNote(TrackDispScheduleRec* Schedule,
  927.                                                 long TrackIndex, MyBoolean* CommandFlag, long PixelX,
  928.                                                 long* FrameIndex)
  929.     {
  930.         MyBoolean                        OnFrame;
  931.         long                                Index;
  932.         FrameObjectRec*            Frame;
  933.         long                                BeginningOfFrame;
  934.         OrdType                            FrameWidth;
  935.  
  936.         CheckPtrExistence(Schedule);
  937.         ERROR((TrackIndex < 0) || (TrackIndex >= TrackDisplayGetNumTracks(Schedule)),
  938.             PRERR(ForceAbort,"TrackDisplayGetUnderlyingNote:  track index out of range"));
  939.         if (!TrackDisplayPixelToIndex(Schedule,Schedule->TrackAttrArray[TrackIndex].TrackObj,
  940.             PixelX,&OnFrame,&Index))
  941.             {
  942.                 *FrameIndex = 0;
  943.                 return NIL;
  944.             }
  945.         if (FrameIndex != NIL)
  946.             {
  947.                 *FrameIndex = Index;
  948.             }
  949.         if (!OnFrame)
  950.             {
  951.                 return NIL;
  952.             }
  953.         if (!TrackDisplayIndexToPixel(Schedule,TrackIndex,Index,&BeginningOfFrame))
  954.             {
  955.                 return NIL;
  956.             }
  957.         Frame = TrackObjectGetFrame(Schedule->TrackAttrArray[TrackIndex].TrackObj,Index);
  958.         FrameWidth = WidthOfFrameAndDraw(0,0,0,0,0,0,Frame,False/*nodraw*/,False);
  959.         *CommandFlag = IsThisACommandFrame(Frame);
  960.         if (*CommandFlag)
  961.             {
  962.                 /* just a single command */
  963.                 if ((BeginningOfFrame <= PixelX) && (BeginningOfFrame + FrameWidth > PixelX))
  964.                     {
  965.                         return GetNoteFromFrame(Frame,0);
  966.                     }
  967.                  else
  968.                     {
  969.                         return NIL;
  970.                     }
  971.             }
  972.          else
  973.             {
  974.                 /* notes are in the frame. */
  975.                 if ((BeginningOfFrame <= PixelX) && (BeginningOfFrame + FrameWidth > PixelX))
  976.                     {
  977.                         long                                NoteIndex;
  978.                         long                                NumFrames;
  979.  
  980.                         /* calculate possible note index */
  981.                         NoteIndex = (PixelX - BeginningOfFrame) / INTERNALSEPARATION;
  982.                         ERROR(NumNotesInFrame(Frame) == 0,PRERR(ForceAbort,
  983.                             "TrackDisplayGetUnderlyingNote:  frame doesn't contain any notes"));
  984.                         NumFrames = NumNotesInFrame(Frame);
  985.                         if (NoteIndex > NumFrames - 1)
  986.                             {
  987.                                 NoteIndex = NumFrames - 1;
  988.                             }
  989.                         return GetNoteFromFrame(Frame,NoteIndex);
  990.                     }
  991.                  else
  992.                     {
  993.                         return NIL;
  994.                     }
  995.             }
  996.     }
  997.  
  998.  
  999. /* find out if a certain frame should be drawn.  this is for squashing command */
  1000. /* frames from channels other than the front channel */
  1001. MyBoolean                            TrackDisplayShouldWeDrawIt(TrackDispScheduleRec* Schedule,
  1002.                                                 long TrackIndex, long FrameIndex)
  1003.     {
  1004.         CheckPtrExistence(Schedule);
  1005.         ERROR((TrackIndex < 0) || (TrackIndex >= TrackDisplayGetNumTracks(Schedule)),
  1006.             PRERR(ForceAbort,"TrackDisplayShouldWeDrawIt:  track index out of range"));
  1007.         if (!TrackDisplayScheduleUpdate(Schedule))
  1008.             {
  1009.                 return False;
  1010.             }
  1011.         PRNGCHK(Schedule->TrackAttrArray[TrackIndex].FrameAttrArray,
  1012.             &(Schedule->TrackAttrArray[TrackIndex].FrameAttrArray[FrameIndex]),
  1013.             sizeof(Schedule->TrackAttrArray[TrackIndex].FrameAttrArray[FrameIndex]));
  1014.         return !Schedule->TrackAttrArray[TrackIndex]
  1015.             .FrameAttrArray[FrameIndex].SquashThisOne;
  1016.     }
  1017.  
  1018.  
  1019. /* find out what index a measure bar should be.  if it shouldn't be a measure */
  1020. /* bar, then it returns -1. */
  1021. long                                    TrackDisplayMeasureBarIndex(TrackDispScheduleRec* Schedule,
  1022.                                                 long FrameIndex)
  1023.     {
  1024.         CheckPtrExistence(Schedule);
  1025.         if (!TrackDisplayScheduleUpdate(Schedule))
  1026.             {
  1027.                 return -1;
  1028.             }
  1029.         PRNGCHK(Schedule->MainTrackMeasureBars,&(Schedule->MainTrackMeasureBars[FrameIndex]),
  1030.             sizeof(Schedule->MainTrackMeasureBars[FrameIndex]));
  1031.         return Schedule->MainTrackMeasureBars[FrameIndex].BarIndex;
  1032.     }
  1033.  
  1034.  
  1035. /* find out the frame index for the specified measure bar.  returns False if it */
  1036. /* couldn't find that one */
  1037. MyBoolean                            TrackDisplayMeasureIndexToFrame(TrackDispScheduleRec* Schedule,
  1038.                                                 long MeasureBarIndex, long* FrameIndexOut)
  1039.     {
  1040.         long                                Scan;
  1041.  
  1042.         CheckPtrExistence(Schedule);
  1043.         if (!TrackDisplayScheduleUpdate(Schedule))
  1044.             {
  1045.                 return False;
  1046.             }
  1047.         for (Scan = 0; Scan < Schedule->TrackAttrArray[0].NumFrames; Scan += 1)
  1048.             {
  1049.                 PRNGCHK(Schedule->MainTrackMeasureBars,&(Schedule->MainTrackMeasureBars[Scan]),
  1050.                     sizeof(Schedule->MainTrackMeasureBars[Scan]));
  1051.                 if (Schedule->MainTrackMeasureBars[Scan].BarIndex == MeasureBarIndex)
  1052.                     {
  1053.                         *FrameIndexOut = Scan;
  1054.                         return True;
  1055.                     }
  1056.             }
  1057.         return False;
  1058.     }
  1059.  
  1060.  
  1061. /* find out if a measure bar should be greyed out or drawn solid.  it returns */
  1062. /* true if the measure bar should be greyed. */
  1063. MyBoolean                            TrackDisplayShouldMeasureBarBeGreyed(
  1064.                                                 TrackDispScheduleRec* Schedule, long FrameIndex)
  1065.     {
  1066.         CheckPtrExistence(Schedule);
  1067.         if (!TrackDisplayScheduleUpdate(Schedule))
  1068.             {
  1069.                 return False;
  1070.             }
  1071.         PRNGCHK(Schedule->MainTrackMeasureBars,&(Schedule->MainTrackMeasureBars[FrameIndex]),
  1072.             sizeof(Schedule->MainTrackMeasureBars[FrameIndex]));
  1073.         ERROR(Schedule->MainTrackMeasureBars[FrameIndex].BarIndex == -1,PRERR(AllowResume,
  1074.             "TrackDisplayShouldMeasureBarBeGreyed:  no measure bar should be drawn here."));
  1075.         return Schedule->MainTrackMeasureBars[FrameIndex].DrawBarGreyed;
  1076.     }
  1077.  
  1078.  
  1079. /* get a list of coordinates that need to be tied together */
  1080. /* it might return NIL if there isn't a tie tracker. */
  1081. struct TieIntersectListRec*    TrackDisplayGetTieIntervalList(TrackDispScheduleRec* Schedule,
  1082.                                                 long StartX, long Width)
  1083.     {
  1084.         TieIntersectListRec*    TheList;
  1085.  
  1086.         CheckPtrExistence(Schedule);
  1087.         if (!TrackDisplayScheduleUpdate(Schedule))
  1088.             {
  1089.                 return NIL;
  1090.             }
  1091.         if (Schedule->TiePixelTracker == NIL)
  1092.             {
  1093.                 return NIL;
  1094.             }
  1095.         TheList = GetTieTrackPixelIntersecting(Schedule->TiePixelTracker,StartX,Width);
  1096.         return TheList;
  1097.     }
  1098.